/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package calcul;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import vue.ObjetEchange;

/**
 * Version multi-thread.
 * Pour faire la version multi, j'ai regarde ce qui ne varie pas dans la boucle en version single.
 * @author Jo
 */
public class MonteCarloMulti extends MonteCarloAbstract{
    
    /**
     * Pour suivre l'avancement du calcul dans la fenetre.
     */
    static int nbCallablesFinies=0;
    int poolSize=1;
    /**
     * Parametre de l'utilisateur. 0 pour Auto.
     */
    int manual_poolsize=0;
    /**
     * Pour voir si le nombre effectif de threads qui travaillent correspond avec le nombre specifie.
     */
    public static HashSet listeThreadsID = new HashSet();
    
    
    public MonteCarloMulti(double S, double X, double r, double T, double b, double v, int nSteps, int nSimulations, int p) {
        super(S, X, r, T, b, v, nSteps, nSimulations);
        manual_poolsize = p <= 0 ? 0 : p;
    }
    
    
    
    @Override
    public void run() {
        super.run();
        
        
        //System.out.println(MonteCarloMulti.listeThreadsID.size());
        
        Fichiers_log.ajouterAuFichierLog(duree_calcul+";"+poolSize, Fichiers_log.Benchmark.TIME_THREADS);
        
    }
    
    
    
    @Override
    double MonteCarloStandardOption(String CallPutFlag, final double S, final double X, double T, 
            double r, double b, double v, final int nSteps, final int nSimulations) 
    {
        double dt, Sum=0;
        int i, z=0;
        
        dt = T / nSteps;
        final double Drift = (b - java.lang.Math.pow(v, 2) / 2) * dt;
        final double vSqrdt = v * java.lang.Math.sqrt(dt);
        if (CallPutFlag.equals("c")) {
            z = 1;
        }
        else if (CallPutFlag.equals("p")) {
            z = -1;
        }
        final int zz = z;
        
        //---
        nbCallablesFinies=0;
        
        final int numberOfCores = Runtime.getRuntime().availableProcessors();
        final double blockingCoefficient = 0.9;
        poolSize = manual_poolsize==0? 
                (int)(numberOfCores / (1 - blockingCoefficient)) : 
                manual_poolsize;
        
        final List<Callable<Double>> partitions = new ArrayList<Callable<Double>>();

        for(i = 1; i<= nSimulations; i++) {
            partitions.add(new Callable<Double>() {
                @Override
                public Double call() throws Exception {
                    listeThreadsID.add(Thread.currentThread().getId());
                    
                    double St = S;
                    for(int j = 1; j<= nSteps; j++){
                        St = St * java.lang.Math.exp( Drift + vSqrdt * new java.util.Random().nextGaussian() );
                    }
                    
                    nbCallablesFinies++;
                    setChanged();
                    notifyObservers(new ObjetEchange(res, false, (float)nbCallablesFinies/nSimulations));
                    
                    return java.lang.Math.max(zz*(St - X),0);
                }
            });
        }
        
        final ExecutorService executorPool = Executors.newFixedThreadPool(poolSize);
        //final ExecutorService executorPool = Executors.newCachedThreadPool();
        List<Future<Double>> valueOfStocks = null;
        try {
            valueOfStocks = executorPool.invokeAll(partitions, 10000, TimeUnit.SECONDS);
        } catch (InterruptedException ex) {
            Logger.getLogger(MonteCarloMulti.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        for(final Future<Double> valueOfAStock : valueOfStocks) {
            try {
                Sum += valueOfAStock.get();
            } catch (InterruptedException ex) {
                Logger.getLogger(MonteCarloMulti.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ExecutionException ex) {
                Logger.getLogger(MonteCarloMulti.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        executorPool.shutdown();
        //---
        
        return java.lang.Math.exp(-r * T) * (Sum /nSimulations);
    }
    
}
